MP3 Player Driver

The MP3 driver serializes the data by sending it in chunks. It uses pointer math to advance pointer through the data in the buffer.

The main buffers are: * The hardcoded data in train_crossing.h is about 38K bytes. * The main buffer is MP3BUFFERSIZE which is defined as 256 bytes. * The VN1002’s FIFO hardware buffer which is 32 bytes long.

The code ready the data in chunks of 256 bytes, then sends the chunk in a series of 32 one byte writes to the FIFO. It checks the status of the FIFO before each series of writes.

The hardware spec for the VN1002 says that you only have to check the FIFO’s status once every 32 bytes. Once you start a write of 32 bytes you can finish it before checking the status again.

Task Code

The task code is using GetMP3 to get blocks of data. And it’s using the driver’s Write function to send those chunks.

// Loop though the data until done
start = TRUE;
while (TRUE)
{
        INT32U len;
        State = 5;
        len = GetMP3(buffer, MP3BUFFERSIZE, start);
        start = FALSE;
        SetLED(1);

        if (len > 0)
        {
                length = len;
                err = Write(hMp3, buffer, &length);
                if (err != OS_DRV_NO_ERR)
                {
                        DEBUGMSG(1,("PlayTask: failed write, err: %d\n\r", err));
                        while (TRUE)
                                ; //park here on error
                }
        }

        // If the length of the data sent was smaller then
        if (len < MP3BUFFERSIZE)
        {
                // We are done with file
                #ifdef CONTINUOUS
                        start = TRUE;
                        OSSemPend(SemPrint, 0, &err);
                        DEBUGMSG(1,("PlayTask: restarting\n\r"));
                        OSSemPost(SemPrint);
                #else           //CONTINUOUS
                        break;
                #endif          //CONTINUOUS
        }
}

GetMP3

GetMP3 gets chunks of data from the MP3 data and places it in the buffer.

  • pBuffer = buffer to fill
  • Length - size to fill
  • Start - TRUE start at begining of file
  • return - number of bytes returned. If less than Length, then end of file.
INT32U GetMP3(INT8U* pBuffer, INT32U Length, BOOLEAN Start)
{
        static INT32U current = 0;
        static INT32U count = 0;
        INT32U len;
        INT32U* ptmp = &current;

        if (Start)
        {
                current = 0;
        }

        count++;
        len = (Length + current) < sizeof(Wave) ? Length : sizeof(Wave) - current;

        if (((current) >= (sizeof(Wave) - 100)) || (len > 256))
        {
                INT32U tmp;
                tmp = len;
        }

        memcpy(pBuffer, &Wave[current], len);

        if (((current) >= (sizeof(Wave) - 100)) || (len > 256))
        {
                INT32U tmp;
                tmp = len;
        }
        current += len;
        return len;
}

MP3 Driver Write Function

The MP3 driver function sends 32 bytes at a time to the VN1002 chip’s FIFO. It checks if the chip is ready to receive by testing it with the MP3_DREG() macro. That macro is simpling readying the MP3_DREQ_BIT from chip’s FIO0PIN memory register.

INT8U Mp3_Write(INT8U DeviceIndex, PVOID pContext, PVOID pBuffer, INT32U* pCount)
{
        INT32U length = *pCount;
        INT8U err;
        INT32U pos;
        INT32U len;

        #define DREQ_CHUNK 32

        *pCount = 0;

        if (DeviceIndex >= MP3_DEVICE_COUNT)
        {
                // someone passed us a bogus device
                DEBUGMSG(TRUE, ("MP3_DRV attempt to Write bad device index: 0x%X\n\r", DeviceIndex));
                return OS_DRV_NO_DEVICE;
        }

        //we need to check DREQ every 32-bytes to not overflow the vn1002 chips FIFO

        for (pos = 0; pos < length; pos += len)
        {
                //send data out SPI port
                if (((PMP3_EXT) pContext)->DataMode)
                {
                        // Wait for ready
                        while (!MP3_DREQ())
                        {
                                // Empty loop lets us spin here until MP3_DREQ says it ready
                        }

                        // Program chip
                        MP3_DCS(1);
                }
                else
                {
                        MP3_CS(1);
                }

                // Calculate size of the data to be sent

                len = ((pos + DREQ_CHUNK) > length) ? length - pos : DREQ_CHUNK;

                // Write data using the SPI driver

                err = Write(((PMP3_EXT) pContext)->hSpi, &(((INT8U*) pBuffer))[pos], &len);

                (((PMP3_EXT) pContext)->DataMode) ? MP3_DCS(0) : MP3_CS(0);

                Delay(10);

                if (err != OS_DRV_NO_ERR)
                {
                        DEBUGMSG(TRUE, ("MP3_DRV failed SPI Write, err: %d\n\r", err));
                        return err;
                }
        }
        *pCount = length;

        return OS_DRV_NO_ERR;
}

MP3 Helper Macros

These are the macros used by the MP3 driver.

//init the GPIO pins we use
#define MP3_DREQ_BIT (1<<10)         /* UEXT-3  txd2 P0.10 == data request output */
#define MP3_RESET_BIT (1<<11)        /* UEXT-4  rxd2 P0.11 == data request output */
#define MP3_CS_BIT   (1<<28)         /* UEXT-5  scl0 P0.28 == cs chip select */
#define MP3_EEPROM_BIT (1<<27)       /* UEXT-6  sda0 P0.27 == sw_cs1 */
#define MP3_DCS_BIT  (1<<6)          /* UEXT-10 ssel P0.06 == dcs data chip select */

#define MP3_CS(x) ((x) ? WRITEREG32(FIO0CLR, MP3_CS_BIT) : WRITEREG32(FIO0SET, MP3_CS_BIT))
#define MP3_DCS(x) ((x) ? WRITEREG32(FIO0CLR, MP3_DCS_BIT) : WRITEREG32(FIO0SET, MP3_DCS_BIT))
#define MP3_RESET(x) ((x) ? WRITEREG32(FIO0CLR, MP3_RESET_BIT) : WRITEREG32(FIO0SET, MP3_RESET_BIT))
#define MP3_EEPROM(x) ((x) ? WRITEREG32(FIO0CLR, MP3_EEPROM_BIT) : WRITEREG32(FIO0SET, MP3_EEPROM_BIT))

// Tests if chip is ready
#define MP3_DREQ() (READREG32(FIO0PIN) & MP3_DREQ_BIT)

SPI Write Function

INT8U Spi_Write(INT8U DeviceIndex, PVOID pContext, PVOID pBuffer, INT32U* pCount)
{
        INT32U length = *pCount;
        INT32U ii;
        PSPI_DEVICE pDevice = &((PSPI_EXT) pContext)->Device[DeviceIndex];
        INT8U tmp;

        *pCount = 0;

        if (DeviceIndex >= SPI_DEVICE_COUNT)
        {
                // someone passed us a bogus device
                DEBUGMSG(TRUE, ("SPI_DRV attempt to Write bad device index: 0x%X\n\r", DeviceIndex));
                return OS_DRV_NO_DEVICE;
        }

        //write out the bytes
        for (ii = 0; ii < length; ii++)
        {
                while (!((tmp = READREG8(pDevice->pSSPnSR)) & SSP_TNF))
                {
                        DEBUGMSG(TRUE, ("SPI_DRV SPSR: 0x%X\n\r", tmp));
                }

                WRITEREG16(pDevice->pSSPnDR, (INT16U)((INT8U*)pBuffer)[ii]);

                while (!((tmp = READREG8(pDevice->pSSPnSR)) & SSP_RNE))
                {
                        DEBUGMSG(TRUE, ("SPI_DRV SPSR: 0x%X\n\r", tmp));
                }

                //read back any input data
                ((INT8U*) pBuffer)[ii] = (INT8U) READREG16(pDevice->pSSPnDR);
        }
        *pCount = length;

        return OS_DRV_NO_ERR;
}

SPI Helper Macro

The SPI Write function use WRITEREG16 to write out on one 8 bit value at a time as a series of 16 bit values. Depends on the size of the register it is writing to.

#define WRITEREG16(pReg, data) ((*(volatile U16 *)(pReg)) = data)